package com.gitee.faster.webdav.kernel.resource.local;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileMode;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.text.StrPool;
import com.gitee.faster.webdav.common.structure.RangeValue;
import com.gitee.faster.webdav.exception.ReadException;
import com.gitee.faster.webdav.kernel.resource.ResourceRepository;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author hongda.li
 */
@Slf4j
public class LocalResourceRepository implements ResourceRepository<LocalResource> {
    /**
     * 默认缓冲区大小 1MB
     */
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;

    @Override
    public boolean accept(String requestPath) {
        // 默认接受所有路径请求
        return true;
    }

    @Override
    public LocalResource transform(String requestPath) {
        return new LocalResource(requestPath);
    }

    @Override
    public LocalResource getRoot() {
        return this.transform(StrPool.SLASH);
    }

    @Override
    public LocalResource getParent(LocalResource resource) {
        return new LocalResource(FileUtil.getParent(resource.getFile(), 1));
    }

    @Override
    public void mkdir(LocalResource resource) {
        FileUtil.mkdir(resource.getFile());
    }

    @Override
    public void delete(LocalResource resource) {
        FileUtil.del(resource.getFile());
    }

    @Override
    public List<LocalResource> walk(LocalResource resource, int depth) {
        if (resource.isDirectory()) {
            // 如果是目录则按深度递归目录下的文件
            try (Stream<Path> stream = Files.walk(resource.getFile().toPath())) {
                return stream.map(path -> new File(path.toUri()))
                        .map(LocalResource::new)
                        .toList();
            } catch (Exception e) {
                throw new ReadException("遍历文件出现错误", e);
            }
        }

        // 如果是文件则直接返回
        return Collections.singletonList(resource);
    }

    @Override
    public void upload(LocalResource resource, InputStream input) {
        OutputStream out = new FileWriter(resource.getFile()).getOutputStream();
        IoUtil.copy(input, out, DEFAULT_BUFFER_SIZE);
    }

    @Override
    public void download(LocalResource resource, OutputStream output) {
        try (InputStream stream = FileUtil.getInputStream(resource.getFile())) {
            stream.transferTo(output);
            log.info("完成全量读取 : {}", resource.getName());
        } catch (Exception e) {
            log.warn("全量读取中断 : {}", e.getMessage());
        }
    }

    @Override
    public void downloadPart(LocalResource resource, OutputStream output, RangeValue range) {
        long end = range.end();
        long start = range.start();
        long contentLength = end - start + 1;

        // 缓冲区大小
        int bufferSize = (int) Math.min(contentLength, DEFAULT_BUFFER_SIZE);

        // 读取指定位置的文件数据
        try (RandomAccessFile raf = FileUtil.createRandomAccessFile(resource.getFile(), FileMode.r)) {
            raf.seek(start);
            byte[] data = new byte[bufferSize];
            while (true) {
                int read = raf.read(data);
                if (read == -1) {
                    break;
                }
                output.write(data, 0, read);
            }
            log.info("完成断点续传 : [{} - {} / {}]", start, end, resource.length());
        } catch (Exception e) {
            log.warn("断点续传中断 : {}", e.getMessage());
        }
    }
}
